﻿/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

using System;
#if NET35
using Lucene.Net.Support.Compatibility;
#else
using System.Collections.Concurrent;
#endif
using System.Diagnostics;
using System.Linq;
using Lucene.Net.Analysis.Tokenattributes;
using Lucene.Net.Index;
using Lucene.Net.Search;
using Lucene.Net.Util;

namespace Lucene.Net.Spatial.Util
{
    public static class CompatibilityExtensions
    {
        private static readonly ConcurrentDictionary<string, IBits> _docsWithFieldCache = new ConcurrentDictionary<string, IBits>();

        /// <summary> <p/>
        /// EXPERT: Instructs the FieldCache to forcibly expunge all entries 
        /// from the underlying caches.  This is intended only to be used for 
        /// test methods as a way to ensure a known base state of the Cache 
        /// (with out needing to rely on GC to free WeakReferences).  
        /// It should not be relied on for "Cache maintenance" in general 
        /// application code.
        /// <p/>
        /// <p/>
        /// <b>EXPERIMENTAL API:</b> This API is considered extremely advanced 
        /// and experimental.  It may be removed or altered w/o warning in future 
        /// releases 
        /// of Lucene.
        /// <p/>
        /// </summary>
        public static void PurgeSpatialCaches(this IFieldCache fc)
        {
            _docsWithFieldCache.Clear();
        }


        public static byte[] ToByteArray(this sbyte[] sbytes)
        {
            return sbytes.Select(Convert.ToByte).ToArray();
        }

        /* table of number of leading zeros in a byte */

        public static readonly byte[] nlzTable =
            {
                8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3
                , 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
                2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1
                , 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
                , 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
                , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
                , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
            };
        public static sbyte[] ToSByteArray(this byte[] bytes)
        {
            return bytes.Select(Convert.ToSByte).ToArray();
        }

        /// <summary>
        /// Returns the number of leading zero bits.
        /// </summary>
        /// <param name="x"></param>
        /// <returns></returns>
        public static int BitUtilNlz(long x)
        {
            int n = 0;
            // do the first step as a long
            var y = (int)((ulong)x >> 32);
            if (y == 0) { n += 32; y = (int)(x); }
            if ((y & 0xFFFF0000) == 0) { n += 16; y <<= 16; }
            if ((y & 0xFF000000) == 0) { n += 8; y <<= 8; }
            return n + nlzTable[(uint)y >> 24];
            /* implementation without table:
              if ((y & 0xF0000000) == 0) { n+=4; y<<=4; }
              if ((y & 0xC0000000) == 0) { n+=2; y<<=2; }
              if ((y & 0x80000000) == 0) { n+=1; y<<=1; }
              if ((y & 0x80000000) == 0) { n+=1;}
              return n;
             */
        }
    }

    public static class Arrays
    {
        public static void Fill<T>(T[] array, int fromIndex, int toIndex, T value)
        {
            if (array == null)
            {
                throw new ArgumentNullException("array");
            }
            if (fromIndex < 0 || fromIndex > toIndex)
            {
                throw new ArgumentOutOfRangeException("fromIndex");
            }
            if (toIndex > array.Length)
            {
                throw new ArgumentOutOfRangeException("toIndex");
            }
            for (var i = fromIndex; i < toIndex; i++)
            {
                array[i] = value;
            }
        }
    }

    /// <summary>
    /// Expert: Every composite-key in the internal cache is of this type.
    /// </summary>
    internal class Entry
    {
        internal readonly String field;        // which Fieldable
        internal readonly Object custom;       // which custom comparator or parser

        /* Creates one of these objects for a custom comparator/parser. */
        public Entry(String field, Object custom)
        {
            this.field = field;
            this.custom = custom;
        }

        /* Two of these are equal iff they reference the same field and type. */
        public override bool Equals(Object o)
        {
            var other = o as Entry;
            if (other != null)
            {
                if (other.field.Equals(field))
                {
                    if (other.custom == null)
                    {
                        if (custom == null) return true;
                    }
                    else if (other.custom.Equals(custom))
                    {
                        return true;
                    }
                }
            }
            return false;
        }

        /* Composes a hashcode based on the field and type. */
        public override int GetHashCode()
        {
            return field.GetHashCode() ^ (custom == null ? 0 : custom.GetHashCode());
        }
    }
}
